#include <common.h>
#include "syscall.h"
#include "fs.h"
#include "proc.h"
#include <sys/time.h>


extern void naive_uload(PCB *pcb, const char* filename);

#ifdef STRACE
static const char* syscall_name_table[] = {
    "exit",  "yield",  "open",   "read",  "write",
    "kill",	 "getpid", "close",  "lseek", "brk",
    "fstat", "time",   "signal", "execve", "fork",
    "link",  "unlink", "wait",   "times",  "gettimeofday"
};
#endif

void sys_exit(Context *c) {
    halt(c->GPR1);
    c->GPRx = 0;
}

void sys_yield(Context *c) {
    yield();
    c->GPRx = 0;
}

void sys_open(Context *c){
    c->GPRx = fs_open((char*)c->GPR2, c->GPR3, c->GPR4);
}

void sys_read(Context *c){
    c->GPRx = fs_read(c->GPR2, (void*)c->GPR3, c->GPR4);
}

void sys_write(Context* c) {
    c->GPRx = fs_write(c->GPR2, (void*)c->GPR3, c->GPR4);
}

void sys_close(Context* c) {
    c->GPRx = fs_close(c->GPR2);
}

void sys_lseek(Context *c){
    c->GPRx = fs_lseek(c->GPR2, c->GPR3, c->GPR4);
}

/**
 * 若上层应用程序请求 SYS_brk 系统调用成功, 该系统调用会返回0; 目前 nanos-lite 只会返回 0
 */
void sys_brk(Context* c) {
    c->GPRx = 0;
}

void sys_gettimeofday(Context* c) {
    struct timeval* tv = (struct timeval *)c->GPR2;
    uint64_t us = io_read(AM_TIMER_UPTIME).us;
    tv->tv_usec = us % 1000000;
    tv->tv_sec = us / 1000000;
    c->GPRx = 0;
}

void sys_execve(Context* c) {
    naive_uload(NULL, (const char*)c->GPR2);
    c->GPRx = 0;
}

void do_syscall(Context *c) {
    uintptr_t a[4];
    a[0] = c->GPR1;
#ifdef STRACE
    Log("syscall [%s]: a0=%#x, a1=%#x, a2=%#x", syscall_name_table[c->GPR1], c->GPR2, c->GPR3, c->GPR4);
#endif
    switch (a[0]) {
#ifdef LIBOS_SYSCALL_H
        case SYS_exit: sys_exit(c); break;
        case SYS_yield: sys_yield(c); break;
        case SYS_open: sys_open(c); break;
        case SYS_read: sys_read(c); break;
        case SYS_write: sys_write(c); break;
        case SYS_close: sys_close(c); break;
        case SYS_lseek: sys_lseek(c); break;
        case SYS_brk: sys_brk(c); break;
        case SYS_gettimeofday: sys_gettimeofday(c); break;
        case SYS_execve: sys_execve(c); break;
#endif
        default: panic("Unhandled syscall ID = %d", a[0]);
    }
#ifdef STRACE
    Log("syscall [%s]: ret=%#x", syscall_name_table[c->GPR1], c->GPRx);
#endif
}
